import time
import pygame
#import numpy as np
from random import randint
from pygame.locals import *


class RandColor(pygame.Color):
    '''
    随机颜色类，直接调用构造函数生成一个随机的pygame.Color
    '''

    def __init__(self):
        super().__init__(randint(0, 255), randint(0, 255), randint(0, 255))


class ChessBoard:
    '''
    棋盘类
    '''
    # 每次解算的停顿事件
    solve_delay: float
    # 棋盘的矩阵，一个 col_num * row_num 的数组
    board_array: list
    # 棋盘列数
    col_num: int
    # 棋盘行数
    row_num: int
    # 棋盘的像素尺寸
    width: int
    height: int
    # 每个棋盘格子的大小
    cell_size: int
    # 棋盘的像素尺寸
    size: tuple
    # 棋盘尺寸 k
    k: int
    # 选中的单元格子，还没用到
    selected: tuple
    # 初始颜色列表，第一个为背景颜色，第二个为第一个点击的棋盘格的颜色
    colors_list: list = [pygame.Color(255, 255, 255), pygame.Color(255, 0, 0)]
    border_color: pygame.Color = pygame.Color(0, 0, 0)
    # 棋盘对应的surface对象
    screen: pygame.Surface
    # 第一次点击的位置
    first_x: int
    first_y: int

    def __init__(self, k=4, CellSize=40, solve_delay: float = 0.2):
        '''
        初始化棋盘，棋盘格数 = 2**(k+1)
        :param k:棋盘的大小
        :param CellSize: 每个棋盘格子的像素大小
        :param solve_delay: 每一步解算的停顿时长
        '''
        self.solve_delay = solve_delay
        self.cell_size = CellSize
        self.col_num = 2 ** k
        self.row_num = 2 ** k
        # 计算棋盘显示窗口的大小
        self.width = self.col_num * self.cell_size
        self.height = self.row_num * self.cell_size
        self.size = (self.width, self.height)
        # 创建一个棋盘数组，各点的初始值为0
        # self.board_array = np.zeros(shape=(self.col_num, self.row_num), dtype=np.uint8)
        self.board_array = [[0 for j in range(self.col_num)] for i in range(self.row_num)]
        # self.board_array = np.ndarray(shape=(self._CellWidth, self._CellHeight), dtype=np.uint8)
        # (f"self.board initial shape:f{self.board_array.shape}")
        # print(self.board_array)

    # def toImage(self):
    #     # return Image.fromarray(self.board_array).convert('RGB').resize(self.size)
    #     img = Image.fromarray(self.board_array).convert('RGB').resize(self.size)
    #     return pygame.image.fromstring(img.tobytes(), self.size, format=)

    def pixToCell(self, pix_x: int, pix_y: int) -> tuple:
        return int(pix_x / self.cell_size), int(pix_y / self.cell_size)

    def selectCell(self, pix_x: int, pix_y: int):
        """
        选择一个棋盘格子
        :param pix_x: 水平坐标，以像素为单位
        :param pix_y: 垂直坐标，以像素为单位
        :return:
        """
        self.selected = self.pixToCell(pix_x, pix_y)
        return self.selected

    def setCellColor(self, color=None, x: int = None, y: int = None, pix_x: int = None, pix_y: int = None):
        '''
        给某一个点设置颜色，现在只接受第一次的点击
        '''
        # 如果是第一个格子，把这个格子设置为红色
        # if self.first_x is None and self.first_y is None and pix_x is not None and pix_y is not None:
        if (not hasattr(self, 'first_x')) and pix_x is not None and pix_y is not None:
            self.first_x, self.first_y = self.pixToCell(pix_x, pix_y)
            self.board_array[self.first_x][self.first_y] = 1
        # else:
        #     if color is None:
        #         self.colors_list.append(RandColor())
        #     if x is not None and y is not None:
        #         self.board_array[x][y] = len(self.colors_list) - 1
        #     elif pix_x is not None and pix_y is not None:
        #         x, y = self.pixToCell(pix_x, pix_y)
        #         self.board_array[x][y] = len(self.colors_list) - 1
        #     else:
        #         raise UserWarning(f"参数传递有误，没有修改任何棋盘格的颜色。x={x},y={y},pix_x={pix_x},pix_y={pix_y}")

    def setScreen(self, screen: pygame.surface):
        '''
        指定棋盘对应的surface对象
        :param screen:
        :return:
        '''
        self.screen = screen

    def update(self):
        assert self.screen, "必须使用setScreen(screen)函数传递一个pygame.surface对象"
        # 绘制矩形
        for i, row in enumerate(self.board_array):
            for j, pix in enumerate(row):
                # 计算出棋盘格子的位置和大小
                rect = i * self.cell_size, j * self.cell_size, (i + 1) * self.cell_size, (j + 1) * self.cell_size
                pygame.draw.rect(screen, self.colors_list[pix], rect)
        # 绘制边框线
        for i, row in enumerate(self.board_array):
            for j, pix in enumerate(row):
                # 和左边的方格颜色进行比较
                if self.board_array[i][j] != self.board_array[i][j - 1] or self.board_array[i][j] == 0:
                    pygame.draw.line(self.screen, self.border_color, (self.cell_size * i, self.cell_size * j),
                                     (self.cell_size * (i + 1), self.cell_size * j))
                # 和上边的方格颜色进行比较
                if self.board_array[i][j] != self.board_array[i - 1][j] or self.board_array[i][j] == 0:
                    pygame.draw.line(self.screen, self.border_color, (self.cell_size * i, self.cell_size * j),
                                     (self.cell_size * i, self.cell_size * (j + 1)))
        pygame.display.update()

    def solve(self):
        if hasattr(self, 'first_x'):
            self.chessBoard(0, 0, self.first_x, self.first_y, self.col_num)

    def chessBoard(self, tr: int, tc: int, dr: int, dc: int, size: int):
        '''
        解算的关键代码
        :param tr: 解算区域的起始行数
        :param tc:解算区域的起始列数
        :param dr:特殊方格的行数
        :param dc:特殊方格的列数
        :param size:解算区域大小
        :return:
        '''
        if size == 1:
            return
        s = size // 2  # 分割棋盘
        self.colors_list.append(RandColor())
        t = len(self.colors_list) - 1  # L型骨牌号
        if dr < tr + s and dc < tc + s:  # 覆盖左上角子棋盘
            # 特殊方格在此棋盘中
            self.chessBoard(tr, tc, dr, dc, s)
        else:  # 此棋盘中无特殊方格
            # 用t号L型骨牌覆盖右下角
            self.board_array[tr + s - 1][tc + s - 1] = t
            # 覆盖其余方格
            self.chessBoard(tr, tc, tr + s - 1, tc + s - 1, s)
            # 覆盖右上角子棋盘
        if dr < tr + s and dc >= tc + s:
            # 特殊方格在此棋盘中
            self.chessBoard(tr, tc + s, dr, dc, s)
        else:  # 此棋盘中无特殊方格
            # 用t号L型骨牌覆盖左下角
            self.board_array[tr + s - 1][tc + s] = t
            # 覆盖其余方格
            self.chessBoard(tr, tc + s, tr + s - 1, tc + s, s)
            # 覆盖左下角子棋盘
        if dr >= tr + s and dc < tc + s:
            # 特殊方格在此棋盘中
            self.chessBoard(tr + s, tc, dr, dc, s)
        else:
            # 用t号L型骨牌覆盖右上角
            self.board_array[tr + s][tc + s - 1] = t
            # 覆盖其余方格
            self.chessBoard(tr + s, tc, tr + s, tc + s - 1, s)
            # 覆盖右下角子棋盘
        if dr >= tr + s and dc >= tc + s:
            # 特殊方格在此棋盘中
            self.chessBoard(tr + s, tc + s, dr, dc, s)
        else:
            # 用t号L型骨牌覆盖左上角
            self.board_array[tr + s][tc + s] = t
            # 覆盖其余方格
            self.chessBoard(tr + s, tc + s, tr + s, tc + s, s)
        # 读取pygame事件但不做处理，防止程序假死
        pygame.event.get()
        # 更新绘制结果
        self.update()

        time.sleep(self.solve_delay)


def event_handle():
    global board, screen
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
        elif event.type == MOUSEBUTTONUP and event.button == 1:
            # 鼠标左键单击的时候，更新一下颜色
            pix_x, pix_y = pygame.mouse.get_pos()
            board.setCellColor(pix_x=pix_x, pix_y=pix_y)
        elif event.type == KEYDOWN:
            pressed_key = pygame.key.get_pressed()
            # 按下空格键开始解算
            if pressed_key[K_SPACE]:
                board.solve()


if __name__ == '__main__':
    # 棋盘初始化
    board = ChessBoard(k=6, CellSize=14, solve_delay=0.001)
    # pygame 初始化
    pygame.init()
    # 设置屏幕尺寸
    print(f"棋盘尺寸：{board.size}")
    screen = pygame.display.set_mode(board.size)
    # 向棋盘传递surface对象
    board.setScreen(screen)
    # 设置窗口标题
    pygame.display.set_caption(
        b'\xe6\xa3\x8b\xe7\x9b\x98\xe8\xa6\x86\xe7\x9b\x96\xe6\xb1\x82\xe8\xa7\xa3 - \xe9\x82\x93\xe6\xb0\xb8\xe7\x9b\x9b'.decode(
            'utf8'))

    while True:
        # # 将numpy数组转为图片绘制到屏幕上
        # screen.blit(board.toImage(), (0, 0))
        # # 更新屏幕
        # pygame.display.update()
        # 处理pygame事件
        event_handle()
        board.update()
        time.sleep(0.01)
